function this = myparse(this, s, opt, optimalOpt)
% myparse  [Not a public function] Parse model code.
%
% Backend IRIS function.
% No help provided.

% -IRIS Toolbox.
% -Copyright (c) 2007-2015 IRIS Solutions Team.

%--------------------------------------------------------------------------

exception.ModelParser.storeFileName(this.FName);

posYN = theparser.blkpos(s,'!measurement_variables');
posXN = theparser.blkpos(s,'!transition_variables');
posEYN = theparser.blkpos(s,'!measurement_shocks');
posEXN = theparser.blkpos(s,'!transition_shocks');
posPN = theparser.blkpos(s,'!parameters');
posGN = theparser.blkpos(s,'!exogenous_variables');
posAllN = [posYN,posXN,posEYN,posEXN,posPN,posGN];

posRQ = theparser.blkpos(s,'!reporting_equations');
posYQ = theparser.blkpos(s,'!measurement_equations');
posXQ = theparser.blkpos(s,'!transition_equations');
posDQ = theparser.blkpos(s,'!dtrends');
posLQ = theparser.blkpos(s,'!links');
posUQ = theparser.blkpos(s,'!sstate_update');
posAQ = theparser.blkpos(s,'!autoexogenise');

if ~opt.declareparameters
    doDeclareParameters();
end


% Variables, shocks and parameters
%==================================

% Read the individual names of variables, shocks, and parameters.
this.name      = [ s(posAllN).name ];
this.nametype  = [ s(posAllN).NameType ];
this.namelabel = [ s(posAllN).namelabel ];
this.namealias = [ s(posAllN).namealias ];


% Log variables
%---------------

this.IxLog = false(size(this.name));
this.IxLog(this.nametype==1) = s(posYN).IxLog;
this.IxLog(this.nametype==2) = s(posXN).IxLog;


% Reporting equations
%=====================
this.Reporting = rpteq(s(posRQ),this.FName);


% Equations
%===========
% There are 5 types of equations: measurement equations, transition
% equations, deterministic trends, dynamic links, and parameter updates.

% Read measurement equations
%----------------------------
isChkLoss = false;
this = xxReadEqtns(this, s(posYQ), isChkLoss);

% Read transition equations
%---------------------------
% Loss function is always moved to the end.
isChkLoss = true;
[this, isLoss, lossDisc] = xxReadEqtns(this, s(posXQ), isChkLoss);

% Check for empty dynamic equations. This may occur if the user types a
% semicolon between the full equations and its steady state version.
doChkEmptyEqtn();

this.multiplier = false(size(this.name));

if  isLoss
    % Create placeholders for new transition names (mutlipliers) and new
    % transition equations (derivatives of the loss function wrt existing
    % variables).
    % Position of loss function in This.eqtn.
    posLossEqtn = NaN;
    % Presence of a nonnegativity constraint.
    isNneg = false;
    % Positions of the nonnegative variable and nonnegative multiplier in
    % This.name.
    posNnegName = [ ];
    posNnegMult = [ ];
    % Name of the multiplier associated with nonegativity constraint.
    nnegMultName = '';
    prefix = optimalOpt.multiplierprefix;
    ix = strncmp(this.name, prefix, length(prefix));
    if any(ix)
        arg = exception.Exception.alternate(prefix, this.name(ix));
        throw( exception.ModelParser.PREFIX_MULTIPLIER, ...
            arg{:} );
    end
    doLossPlaceHolders( );
end

% Read deterministic trend equations
%------------------------------------
this = xxReadDtrends(this, s(posDQ));

% Read dynamic links
%--------------------
this = xxReadLinks(this, s(posLQ));

% Read parameter updates
%------------------------
this = xxReadUpdates(this, s(posUQ));

% Read autoexogenise definitions (variable/shock pairs)
%-------------------------------------------------------
[this,ixValid,multiple] = ...
    myautoexogenise(this,s(posAQ).EqtnLhs,s(posAQ).EqtnRhs);
if any(~ixValid)
    throw(  ...
        exception.ModelParser.AUTOEXOGENIZE_INVALID_DEFINITION, ...
        s(posAQ).eqtn{~ixValid} ...
        );
end
if ~isempty(multiple)
    throw(  ...
        exception.ModelParser.AUTOEXOGENIZE_MUTLIPLE_SHOCK, ...
        multiple{:} ...
        );
end

% Process equations
%-------------------
nName = length(this.name);
nEqtn = length(this.eqtn);

% Remove ! from math functions.
% This is for bkw compatibility only.
this.eqtnF = strrep(this.eqtnF,'!','');
if ~this.IsLinear
    this.EqtnS = strrep(this.EqtnS,'!','');
end

% Remove blank spaces.
this.eqtn = regexprep(this.eqtn,{'\s+','".*?"'},{'',''});
this.eqtnF = regexprep(this.eqtnF,'\s+','');
if ~this.IsLinear
    this.EqtnS = regexprep(this.EqtnS,'\s+','');
end

% Make sure all equations end with semicolons.
for iEq = 1 : length(this.eqtn)
    if ~isempty(this.eqtn{iEq}) && this.eqtn{iEq}(end) ~= ';'
        this.eqtn{iEq}(end+1) = ';';
    end
    if ~isempty(this.eqtnF{iEq}) && this.eqtnF{iEq}(end) ~= ';'
        this.eqtnF{iEq}(end+1) = ';';
    end
    if ~isempty(this.EqtnS{iEq}) && this.EqtnS{iEq}(end) ~= ';'
        this.EqtnS{iEq}(end+1) = ';';
    end
end

% Max lag and lead
%------------------
maxSh = max([s.MaxSh]);
minSh = min([s.MinSh]);
if isLoss
    % Anticipate that multipliers will have leads as far as the greatest
    % lag, and lags as far as the greatest lead.
    maxSh = maxSh - minSh;
    minSh = minSh - maxSh;    
end
maxSh = maxSh + 1;
minSh = minSh - 1;
this.Shift = minSh : maxSh;
nt = maxSh - minSh + 1;

% Replace variables names with codes
%------------------------------------
% Check for sstate references occuring in wrong places. Also replace
% the old syntax & with $.
doChkSstateRef( );

this.occur = false(nEqtn,nName*nt);
this.occurS = false(nEqtn,nName);

[namePtn,nameRplF,nameRplS] = mynamepattrepl(this);

% Steady-state equations
%------------------------
if ~this.IsLinear
    % If no steady-state version exists, copy the full equation.
    isEmptySstate = cellfun(@isempty,this.EqtnS) & this.eqtntype <= 2;
    this.EqtnS(isEmptySstate) = this.eqtnF(isEmptySstate);
    this.EqtnS(this.eqtntype > 2) = {''};
    if isLoss
        % Do not copy the loss function to steady state equations.
        this.EqtnS{posLossEqtn} = '';
    end
    
    this.EqtnS = regexprep(this.EqtnS,namePtn,nameRplS);
    
    % Remove steady-state references from steady-state equations; they are
    % treated as the respective variables.
    this.EqtnS = strrep(this.EqtnS,'&%','%');
    this.EqtnS = strrep(this.EqtnS,'&#','#');
    % Handle sstate references to shocks, which have become &0 by now.
    this.EqtnS = strrep(this.EqtnS,'&0','0');
    
    % Replace ?(!10) with g(10).
    this.EqtnS = strrep(this.EqtnS,'?(!','g(');

    % Remove control codes from steady-state equations.
    this.EqtnS = strrep(this.EqtnS,'%(','x(');
    this.EqtnS = strrep(this.EqtnS,'(!','(');
    this.EqtnS = strrep(this.EqtnS,'{@','{');
    
    % Replace
    % * `log(exp(x(...)))` with `x(...)`;
    % * `log(exp(x(...){-2}))` with `x(...){-2}`;
    % This helps a lot.
%     This.EqtnS = regexprep(This.EqtnS, ...
%         'log\(exp\((x\(\d+\)((\{[\+\-]\d+\})?))\)\)', ...
%         '$1');
    
    % Leave lags and leads in sstate equations *semifinished*, e.g.
    % `x(15){-1}`. They are finalized immediately before evaluation since the
    % final form of the equations depend on the log status of each variable.
    % The log status can be now altered in the model object by the user.
    
else
    this.EqtnS(:) = {''};
end

% Full equations
%----------------
this.eqtnF = regexprep(this.eqtnF,namePtn,nameRplF);

% Replace %(:,!10,@){@+2} with %(:,!10,@+2) to complete time subscripts.
this.eqtnF = strrep(this.eqtnF,'@){@','@');
this.eqtnF = strrep(this.eqtnF,'}',')');

% Try to catch undeclared names in all equations except dynamic links at
% this point; all valid names have been substituted for by %(...) and
% ?(...). Do not do it in dynamic links because the links can contain std
% and corr names which have not been substituted for.
doChkUndeclared();

% Replace control codes in full equations.
% * Exogenous variables `?(!15,:)` -> `g(!15,:)`.
% * Variables, parameters, shocks `%(:,!15,@+5)` -> `x(:,!15,@+5)`.
% * Steady-state references `&x(:,!15,@+5)` -> `L(:,!15,@+5)`.
% * Time subscripts `@+5` -> `t+5`.
% * Remove `!` from name positions.
this.eqtnF = strrep(this.eqtnF,'?(!','g(');
this.eqtnF = strrep(this.eqtnF,'%(','x(');
this.eqtnF = strrep(this.eqtnF,'&x(','L(');
this.eqtnF = strrep(this.eqtnF,',@',',t');
this.eqtnF = strrep(this.eqtnF,'(:,!','(:,');

if isLoss
    % Retrieve and remove the expression for the discount factor from the
    % optimal policy equation.
    close = textfun.matchbrk(this.eqtnF{posLossEqtn});
    lossDisc = this.eqtnF{posLossEqtn}(2:close-1);
    if isempty(lossDisc)
        throw( exception.ModelParser.LOSS_FUNC_DISCOUNT_EMPTY );
    end
    this.eqtnF{posLossEqtn} = this.eqtnF{posLossEqtn}(close+1:end);
end

% Check for orphan { and & after we have substituted for the valid
% references.
doChkTimeSsref( );

% Find the occurences of variable, shocks, and parameters in individual
% equations, including the loss function and its discount factor. The
% occurences in the loss function will be replaced later with the
% occurences in the Lagrangian derivatives.
this = myoccurrence(this,Inf); 

% Check equation syntax before we compute optimal policy but after we
% remove the header min(...) from the loss function equation.
if opt.chksyntax
    mychksyntax(this);
end

% Check the model structure -- part 1 before the loss function is processed.
xxChkStructure1(this);

if isLoss
    % Create optimal policy equations by adding the derivatives of the
    % Lagrangian wrt to the original transition variables. These `naddeqtn` new
    % equation will be put in place of the loss function and the `naddeqtn-1`
    % empty placeholders.
    [newEqtn,newEqtnF,newEqtnS,NewNonlin] = myoptpolicy(this, ...
        posLossEqtn,lossDisc,posNnegName,posNnegMult,optimalOpt.type);

    % Add the new equations to the model object, and parse them.
    last = find(this.eqtntype==2,1,'last');
    this.eqtn(posLossEqtn:last) = newEqtn(posLossEqtn:last);
    this.eqtnF(posLossEqtn:last) = newEqtnF(posLossEqtn:last);
    
    if ~this.IsLinear
        % Add sstate equations. Note that we must at least replace the old equation
        % in `lossPos` position (which was the objective function) with the new
        % equation (which is a derivative wrt to the first variables).
        this.EqtnS(posLossEqtn:last) = newEqtnS(posLossEqtn:last);
        % Update the nonlinear equation flags.
        this.IxNonlin(posLossEqtn:last) = NewNonlin(posLossEqtn:last);
    end
    
    % Update occ arrays to include the new equations.
    this = myoccurrence(this,posLossEqtn:last);
end

% Finishing touches
%-------------------
% Sparse occurence arrays.
this.occur = sparse(this.occur(:,:));
this.occurS = sparse(this.occurS);

% Check the model structure -- part 2 after the loss function is processed.
xxChkStructure2(this);

% Vectorise operators in full equations; this is needed in numeric
% differentiation.
this.eqtnF = textfun.vectorise(this.eqtnF);

% Retype shocks.
this.nametype = floor(this.nametype);

% Reset file name in exception.ModelParser.
exception.ModelParser.storeFileName( );

return

    
    
    
    
    function doChkTimeSsref()
        % Check for { in full and steady-state equations.
        isGrowth = false;
        eqtnS = myfinaleqtns(this,isGrowth);
        inx = ~cellfun(@isempty,strfind(this.eqtnF,'{')) ...
            | ~cellfun(@isempty,strfind(eqtnS,'{'));
        if any(inx)
            throw( exception.ModelParser.MISPLACED_TIME_SUBSCRIPT, ...
                this.eqtn{inx} );
        end
        % Check for & and $ in full and steady-state equations.
        inx = ~cellfun(@isempty,strfind(this.eqtnF,'&')) ...
            | ~cellfun(@isempty,strfind(eqtnS,'&'));
        if any(inx)
            throw( exception.ModelParser.MISPLACED_STEADY_REFERENCE, ...
                this.eqtn{inx} );
        end
    end


%**************************************************************************


    function doDeclareParameters()
        % All declared names except parameters.
        posAllNxP = [posYN,posXN,posEYN,posEXN,posGN];
        declaredNames = [s(posAllNxP).name];
        
        % All names occuring in transition and measurement equations.
        allEqtn = [s([posYQ,posXQ]).eqtn];
        allEqtn = [allEqtn{:}];
        allNames = regexp(allEqtn,'\<[A-Za-z]\w*\>(?![\(\.])','match');
        allNames = unique(allNames);
        
        % Residual names.
        addNames = setdiff(allNames,declaredNames);

        % Re-create parameter declaration section.
        nAdd = length(addNames);
        s(posPN).name = addNames;
        s(posPN).NameType = 4*ones(1,nAdd);
        tempCell = cell(1,nAdd);
        tempCell(:) = {''};
        s(posPN).namelabel = tempCell;
        s(posPN).namealias = tempCell;
        s(posPN).NameValue = tempCell;
        s(posPN).IxLog = false(1,nAdd);
    end % doDeclareParameters()


%**************************************************************************


    function doChkUndeclared()
        % Undeclared names have not been substituted for by the name codes, except
        % std and corr names in dynamic links (std and corr names cannot be used in
        % other types of equations). Undeclared names in dynamic links will be
        % caught in `mychksyntax`. Distinguish variable names from function names
        % (func names are immediately followed by an opening bracket).
        % Unfortunately, `regexp` interprets high char codes as \w, so we need to
        % explicitly type the ranges.
        
        list = regexp(this.eqtnF(this.eqtntype < 4), ...
            '\<[a-zA-Z]\w*\>(?![\(\.])','match');
        
        if isempty([ list{:} ])
            return
        end
        
        lsUndeclared = { };
        lsStdCorr = { };
        
        ixEmptyList = cellfun(@isempty,list);
        for iiEqtn = find(~ixEmptyList)
            
            iiList = unique(list{iiEqtn});
            iiList(strcmp(iiList,'ttrend')) = [ ];
            if isempty(iiList)
                continue
            end
            
            for jj = 1 : length(iiList)
                if strncmp(iiList{jj},'std_',4) ...
                        || strncmp(iiList{jj},'corr_',5)
                    lsStdCorr{end+1} = iiList{jj}; %#ok<AGROW>
                    lsStdCorr{end+1} = this.eqtn{iiEqtn}; %#ok<AGROW>
                else
                    lsUndeclared{end+1} = iiList{jj}; %#ok<AGROW>
                    lsUndeclared{end+1} = this.eqtn{iiEqtn}; %#ok<AGROW>
                end
            end
        end
        
        % Report std or corr names used in equations other than links.
        if ~isempty(lsStdCorr)
            throw( exception.ModelParser.STD_CORR_IN_OTHER_THAN_LINK, ...
                lsStdCorr{:} );
        end
        
        % Report non-function names that have not been declared.
        if ~isempty(lsUndeclared)
            throw( exception.ModelParser.UNDECLARED_MISTYPED_NAME, ...
                lsUndeclared{:} );
        end
    end % doChkUndeclared()


%**************************************************************************


    function doChkSstateRef()
        % Check for sstate references in wrong places.
        func = @(c) ~cellfun(@(x) isempty(strfind(x,'&')),c);
        ixSstateRef = func(this.eqtnF) | func(this.EqtnS);
        % Not allowed in linear models.
        if this.IsLinear
            if any(ixSstateRef)
                throw( exception.ModelParser.SSTATE_REF_IN_LINEAR, ...
                    this.eqtn{ixSstateRef} );
            end
            return
        end
        % Not allowed in deterministic trends.
        inx = ixSstateRef & this.eqtntype==3;
        if any(inx)
            throw( exception.ModelParser.SSTATE_REF_IN_DTREND, ...
                this.eqtn{inx} );
        end
        % Not allowed in dynamic links.
        inx = ixSstateRef & this.eqtntype==4;
        if any(inx)
            throw( exception.ModelParser.SSTATE_REF_IN_LINK, ...
                this.eqtn{inx} );
        end
    end % doChkSstateRef()


%**************************************************************************


    function doLossPlaceHolders()
        % Add new variables, i.e. the Lagrange multipliers associated with all of
        % the existing transition equations except the loss function. These new
        % names will be ordered first -- the final equations will be ordered as
        % derivatives of the lagrangian wrt to the individual variables.
        nAddEqtn = sum(this.nametype==2) - 1;
        nAddName = sum(this.eqtntype==2) - 1;
        % The default name is `Mu_Eq%g` but can be changed through the
        % option `'multiplierName='`.
        newName = cell(1,nAddName);
        for ii = 1 : nAddName
            newName{ii} = [ ...
                optimalOpt.multiplierprefix, ...
                sprintf('Eq%g',ii) ...
                ];
        end
        isNneg = ~isempty(optimalOpt.nonnegative);
        if isNneg
            nAddEqtn = nAddEqtn + 1;
            nAddName = nAddName + 1;
            nnegMultName = [ ...
                optimalOpt.multiplierprefix, ...
                optimalOpt.nonnegative ...
                ];
            newName{end+1} = nnegMultName;
        end
 
        % Insert the new names between at the beginning of the block of existing
        % transition variables.
        ixPre = this.nametype<2;
        ixPost = this.nametype>=2;
        this.name = [ ...
            this.name(ixPre), ...
            newName, ...
            this.name(ixPost) ];
        this.nametype = [ ...
            this.nametype(ixPre), ...
            2*ones(1,nAddName), ...
            this.nametype(ixPost) ];
        this.namelabel = [ ...
            this.namelabel(ixPre), ...
            repmat({''},1,nAddName), ...
            this.namelabel(ixPost) ];
        this.namealias = [ ...
            this.namealias(ixPre), ...
            repmat({''},1,nAddName), ...
            this.namealias(ixPost) ];
        this.IxLog = [ ...
            this.IxLog(ixPre), ...
            false(1,nAddName), ...
            this.IxLog(ixPost) ];
        this.multiplier = [ ...
            this.multiplier(ixPre), ...
            true(1,nAddName), ...
            this.multiplier(ixPost) ];
        % Loss function is always ordered last among transition equations.
        posLossEqtn = length(this.eqtn);
        if isNneg
            % Find the position of nonnegative variables in the list of names AFTER we
            % have inserted placeholders.
            ixNonneg = strcmp(optimalOpt.nonnegative, this.name);
            if ~any(ixNonneg) || this.nametype(ixNonneg)~=2
                throw( exception.ModelParser.NAME_CANNOT_BE_NONNEGATIVE, ...
                    optimalOpt.nonnegative );
            end
            posNnegName = find(ixNonneg);
            posNnegMult = find(strcmp(this.name,nnegMultName));
        end
        % We will add `nAddEqtn` new transition equations, i.e. the
        % derivatives of the Lagrangian wrt the existing transition
        % variables. At the same time, we will remove the loss function so
        % we need to create only `nAddEqtn-1` placeholders.
        this.eqtn(end+(1:nAddEqtn)) = {''};
        this.eqtnF(end+(1:nAddEqtn)) = {''};
        this.EqtnS(end+(1:nAddEqtn)) = {''};
        this.eqtnlabel(end+(1:nAddEqtn)) = {''};
        this.eqtnalias(end+(1:nAddEqtn)) = {''};
        this.IxNonlin(end+(1:nAddEqtn)) = false;
        this.eqtntype(end+(1:nAddEqtn)) = 2;        
    end % doLossPlaceHolders()


%**************************************************************************


    function doChkEmptyEqtn()
        % dochkemptyeqtn  Check for empty full equations.
        emptyInx = cellfun(@isempty,this.eqtnF);
        if any(emptyInx)
            throw( exception.ModelParser.DYNAMIC_EQUATION_EMPTY, ...
                this.eqtn{emptyInx} );
        end
    end
end


% Subfunctions...


%**************************************************************************


function [this, isLoss, lossDisc] = xxReadEqtns(this, s, isChkLoss)
% xxReadEqtns  Read measurement or transition equations.

isLoss = false;
lossDisc = '';
isLossNonlin = false;

if isempty(s.eqtn)
    return
end

% Check for a loss function and its discount factor first if requested by
% the caller. This is done for transition equations only.
if isChkLoss
    doLossFunc( );
end

eqtn = s.eqtn;
eqtnLabel = s.eqtnlabel;
eqtnAlias = s.eqtnalias;

ixNonlin = strcmp(s.EqtnSign, '=#');
if isLoss && isLossNonlin
    % Mark loss function as nonlinear; this is used in myoptpolicy( ).
    ixNonlin(end) = true;
end

nEqtn = length(s.eqtn);
eqtnF = textfun.emptycellstr(1,nEqtn);
eqtnS = textfun.emptycellstr(1,nEqtn);
for iEq = 1 : nEqtn
    if ~isempty(s.EqtnLhs{iEq})
        sign = '+';
        if any(s.EqtnRhs{iEq}(1) == '+-')
            sign = '';
        end
        eqtnF{iEq} = ['-(',s.EqtnLhs{iEq},')',sign,s.EqtnRhs{iEq}];
    else
        eqtnF{iEq} = s.EqtnRhs{iEq};
    end
    if ~isempty(s.SstateRhs{iEq})
        if ~isempty(s.SstateLhs{iEq})
            sign = '+';
            if any(s.SstateRhs{iEq}(1) == '+-')
                sign = '';
            end
            eqtnS{iEq} = ['-(',s.SstateLhs{iEq},')',sign,s.SstateRhs{iEq}];
        else
            eqtnS{iEq} = s.SstateRhs{iEq};
        end
    end
end

this.eqtn(end+(1:nEqtn)) = eqtn;
this.eqtnF(end+(1:nEqtn)) = eqtnF;
this.EqtnS(end+(1:nEqtn)) = eqtnS;
this.eqtnlabel(end+(1:nEqtn)) = eqtnLabel;
this.eqtnalias(end+(1:nEqtn)) = eqtnAlias;
this.eqtntype(end+(1:nEqtn)) = s.EqtnType;
this.IxNonlin(end+(1:nEqtn)) = ixNonlin;

return




    function doLossFunc()
        % doLossFunc  Find loss function amongst equations. The loss function
        % starts with `min(` or `min#(`, the equation must not contain an equal
        % sign (i.e. the LHS must be empty), and the parentheses in min(...) must
        % not contain a comma.
        tkn = regexp( ...
            s.EqtnRhs, ...
            '^(min#?)\(([^,;\)]*)\)', ...
            'once', 'tokens' ...
            );
        ixLoss = ~cellfun(@isempty,tkn) & cellfun(@isempty,s.EqtnLhs);
        if sum(ixLoss)==1
            isLoss = true;
            tkn = tkn{ixLoss};
            % Order the loss function last.
            list = { ...
                'eqtn', 'eqtnlabel', 'eqtnalias', ...
                'EqtnLhs', 'EqtnRhs', 'EqtnSign', ...
                'SstateLhs', 'SstateRhs', 'SstateSign' ...
                };
            for i = 1 : length(list)
                s.(list{i}) = [ ...
                    s.(list{i})(~ixLoss), ...
                    s.(list{i})(ixLoss), ...
                    ];
            end
            s.EqtnLhs{end} = '';
            % S.EqtnRhs{end} = strrep(S.EqtnRhs{end},'#','');
            % Remove the min(...) term from the equation.
            %s.EqtnRhs{end}(from:to) = '';
            isLossNonlin = strcmp(tkn{1}, 'min#');
            % Remove min or min3 from the equation; keep the rest including
            % the discount factor since the discount factor expression
            % needs to be parsed with the rest of the equation.
            s.EqtnRhs{end}(1:3) = '';
            if isLossNonlin
                s.EqtnRhs{end}(1) = '';
            end
        elseif sum(ixLoss)>1
            throw( exception.ModelParser.MULTIPLE_LOSS_FUNC );
        end
    end
end


%**************************************************************************


function this = xxReadDtrends(this, s)
nName = sum(this.nametype==1);
eqtn = textfun.emptycellstr(1,nName);
eqtnF = textfun.emptycellstr(1,nName);
eqtnlabel = textfun.emptycellstr(1,nName);
eqtnalias = textfun.emptycellstr(1,nName);

% Create list of measurement variable names against which the LHS of
% dtrends equations will be matched. Add log(...) for both log-plus and
% log-minus variables.
list = this.name(this.nametype==1);
ixLog = this.IxLog(this.nametype==1);
logList = list;
logList(ixLog) = strcat('log(',logList(ixLog),')');

nEqtn = length(s.eqtn);
ixLogMissing = false(1,nEqtn);
ixInvalid = false(1,nEqtn);
ixMultiple = false(1,nEqtn);
for iEq = 1 : length(s.eqtn)
    ix = strcmp(logList,s.EqtnLhs{iEq});
    if ~any(ix)
        if any(strcmp(list,s.EqtnLhs{iEq}))
            ixLogMissing(iEq) = true;
        else
            ixInvalid(iEq) = true;
        end
        continue
    end
    if ~isempty(eqtn{ix})
        ixMultiple(iEq) = true;
        continue
    end
    eqtn{ix} = s.eqtn{iEq};
    eqtnF{ix} = s.EqtnRhs{iEq};
    eqtnlabel{ix} = s.eqtnlabel{iEq};
    eqtnalias{ix} = s.eqtnalias{iEq};
end

if any(ixLogMissing)
    throw( exception.ModelParser.LHS_VARIABLE_MUST_LOG_IN_DTREND, ...
        s.eqtn{ixLogMissing} );
end
if any(ixInvalid)
    throw( exception.ModelParser.INVALID_LHS_DTREND, ...
        s.eqtn{ixInvalid} );
end
if any(ixMultiple)
    lsMultiple = unique(s.EqtnLhs(ixMultiple));
    throw( exception.ModelParser.MULTIPLE_LHS_DTREND, ...
        lsMultiple{:} );
end

this.eqtn(end+(1:nName)) = eqtn;
this.eqtnF(end+(1:nName)) = eqtnF;
this.EqtnS(end+(1:nName)) = {''};
this.eqtnlabel(end+(1:nName)) = eqtnlabel;
this.eqtnalias(end+(1:nName)) = eqtnalias;
this.eqtntype(end+(1:nName)) = 3;
this.IxNonlin(end+(1:nName)) = false;
end


%**************************************************************************


function this = xxReadLinks(this, s)
if isempty(s.eqtn)
    return
end
nEqtn = length(s.eqtn);
nName = length(this.name);
ixValid = false(1,nEqtn);
posRefresh = model.POS_FUNC(1,nEqtn);
inxE = floor(this.nametype) == 3;
for iEq = 1 : nEqtn
    if isempty(s.eqtn{iEq})
        continue
    end
    [ixAssign,ixStdCorr] = modelobj.mynameix( ...
        this.name,this.name(inxE),s.EqtnLhs{iEq});
    %index = strcmp(This.name,S.EqtnLhs{iEq});
    if any(ixAssign)
        % The LHS name is a variable, shock, or parameter name.
        ixValid(iEq) = true;
        posRefresh(iEq) = find(ixAssign);
    elseif any(ixStdCorr)
        % The LHS name is a std or corr name.
        ixValid(iEq) = true;
        posRefresh(iEq) = nName + find(ixStdCorr);
    end
end
if any(~ixValid)
    throw( exception.ModelParser.INVALID_LHS_LINK, ...
        s.eqtn{~ixValid} );
end
this.eqtn(end+(1:nEqtn)) = s.eqtn;
this.eqtnF(end+(1:nEqtn)) = s.EqtnRhs;
this.EqtnS(end+(1:nEqtn)) = {''};
this.eqtnlabel(end+(1:nEqtn)) = s.eqtnlabel;
this.eqtnalias(end+(1:nEqtn)) = s.eqtnalias;
this.eqtntype(end+(1:nEqtn)) = 4;
this.IxNonlin(end+(1:nEqtn)) = false;
this.Refresh = posRefresh;
end


%**************************************************************************


function this = xxReadUpdates(this, s)
nEqtn = length(s.eqtn);
posUpdate = model.POS_FUNC(1,nEqtn);
ixValidLhs = true(1,nEqtn);
% LHS must be `name{+1}`, where name is parameter name (not std or corr
% name).
nameLhs = regexp(s.EqtnLhs,'^([a-zA-Z]\w*)\{@\+1\}$','tokens','once');
for iEq = 1 : nEqtn
    if isempty(nameLhs{iEq})
        ixValidLhs(iEq) = false;
        continue
    end
    ix = strcmp( nameLhs{iEq}{1}, this.name );
    ixValidLhs(iEq) = any(ix) && this.nametype(ix)==4;
    if ~ixValidLhs(iEq)
        continue
    end
    posUpdate(iEq) = find(ix);
end
if any(~ixValidLhs)
    throw( exception.ModelParser.INVALID_LHS_UPDATE, ...
        s.eqtn{~ixValidLhs} );
end
this.eqtn(end+(1:nEqtn)) = s.eqtn;
this.eqtnF(end+(1:nEqtn)) = s.EqtnRhs;
this.EqtnS(end+(1:nEqtn)) = {''};
this.eqtnlabel(end+(1:nEqtn)) = s.eqtnlabel;
this.eqtnalias(end+(1:nEqtn)) = s.eqtnalias;
this.eqtntype(end+(1:nEqtn)) = 5;
this.IxNonlin(end+(1:nEqtn)) = false;
this.PosUpdate = posUpdate;
end


%**************************************************************************


function xxChkStructure1(This)

nEqtn = length(This.eqtn);
nName = length(This.name);
t0 = find(This.Shift == 0);
occurF = This.occur;
if size(occurF,3) == 1
   nt = size(occurF,2) / nName;
   occurF = reshape(full(occurF),[nEqtn,nName,nt]);
end
occurS = This.occurS;

% Lags and leads.
tt = true(1,size(occurF,3));
tt(t0) = false;

flNameType = floor(This.nametype);

% Lags/leads of measurement variables
%-------------------------------------
inx = any( any( occurF(:, This.nametype==1, tt), 3), 2);
if any(inx)
    throw( exception.ModelParser.MEASUREMENT_SHIFT, ...
        This.eqtn{inx} );
end

% Lags/leads of shocks
%----------------------
inx = any( any( occurF(:, This.nametype==3, tt), 3), 2);
if any(inx)
    throw( exception.ModelParser.SHOCK_SHIFT, ...
        This.eqtn{inx} );
end

% Lags/leads of parameters
%--------------------------
inx = any( any( occurF(:, This.nametype==4, tt), 3), 2);
if any(inx)
    throw( exception.ModelParser.PARAMETER_SHIFT, ...
        This.eqtn{inx} );
end

% Lags and leads of exogenous variables are captured as misplaced time
% subscripts.

% Measurement variables in transition equations.
inx = any( any(occurF(This.eqtntype==2, This.nametype==1,:), 3), 2);
if any(inx)
    eqtn = This.eqtn(This.eqtntype==2);
    throw( exception.ModelParser.MEASUREMENT_VARIABLE_IN_TRANSITION, ...
        eqtn{inx} );
end

% No leads of transition variables in measurement equations.
tt = true(1,size(occurF,3));
tt(1:t0) = false;
inx = any( any(occurF(This.eqtntype==1, This.nametype==2, tt), 3), 2);
if any(inx)
    eqtn = This.eqtn(This.eqtntype==1);
    throw( exception.ModelParser.LEAD_OF_TRANSITION_IN_MEASUREMENT, ...
        eqtn{inx} );
end

% No current date of measurement variable in some measurement equation.
inx = ~any( occurF(This.eqtntype==1, This.nametype==1, t0), 2);
if any(inx)
    eqtn = This.eqtn(This.eqtntype==1);
    throw( exception.ModelParser.NO_CURRENT_MEASUREMENT, ...
        eqtn{inx} );
end

if any(flNameType==3)
    % Find transition shocks in measurement equations.
    inx = any( occurF(This.eqtntype==1, round(10*This.nametype)==32, t0), 2);
    if any(inx)
        eqtn = This.eqtn(This.eqtntype==1);
        throw( exception.ModelParser.TRANSITION_SHOCK_IN_MEASUREMENT, ...
            eqtn{inx} );
    end
    % Find measurement shocks in transition equations.
    inx = any(occurF(This.eqtntype==2, round(10*This.nametype)==31, t0),1);
    if any(inx)
        eqtn = This.eqtn(This.eqtntype==2);
        throw( exception.ModelParser.MEASUREMENT_SHOCK_IN_TRANSITION, ...
            eqtn{inx} );
    end
end

% Names other than parameters and exogenous variables in dtrend equations.
rows = This.eqtntype==3;
cols = This.nametype<4;
inx = any( any( occurF(rows,cols,:), 3), 2);
if any(inx)
    eqtn = This.eqtn(rows);
    throw( exception.ModelParser.OTHER_THAN_PARAMETER_EXOGENOUS_IN_DTREND, ...
        eqtn{inx} );
end

% Exogenous variables only in dtrend equations.
rows = This.eqtntype~=3;
cols = This.nametype==5;
inx = any( any( occurF(rows,cols,:), 3), 2) | any( occurS(rows,cols), 2);
if any(inx)
    eqtn = This.eqtn(rows);
    throw( exception.ModelParser.EXOGENOUS_IN_OTHER_THAN_DTREND, ...
        eqtn{inx} )
end

end




function xxChkStructure2(This)
nEqtn = length(This.eqtn);
nName = length(This.name);
t0 = find(This.Shift == 0);
occurF = This.occur;
if size(occurF,3) == 1
   nt = size(occurF,2) / nName;
   occurF = reshape(full(occurF),[nEqtn,nName,nt]);
end

% No transition variable
%------------------------
if ~any(This.nametype==2)
    throw( exception.ModelParser.NO_TRANSITION_VARIABLE );
end

% No transition equation
%------------------------
if ~any(This.eqtntype==2)
    throw( exception.ModelParser.NO_TRANSITION_EQUATION );
end

% Current dates of transition variables
%---------------------------------------
% Dynamic equations.
aux = ~any(occurF(This.eqtntype==2, This.nametype==2, t0), 1);
if any(aux)
    name = This.name(This.nametype==2);
    throw( exception.ModelParser.NO_CURRENT_DATE_IN_DYNAMIC, ...
        name{aux} );
end
% Steady equations.
if ~This.IsLinear
    aux = ~any(This.occurS(This.eqtntype==2, This.nametype==2), 1);
    if any(aux)
        name = This.name(This.nametype==2);
        throw( exception.ModelParser.NO_CURRENT_DATE_IN_STEADY, ...
            name{aux} );
    end
end

% Current dates of measurement variables
%----------------------------------------
% Dynamic equations.
inx = ~any(occurF(This.eqtntype==1, This.nametype==1, t0), 1);
if any(inx)
    name = This.name(This.nametype==1);
    throw( exception.ModelParser.NO_CURRENT_DATE_IN_DYNAMIC, ...
        name{inx} );
end
% Steady equations.
if ~This.IsLinear
    inx = ~any(This.occurS(This.eqtntype==1, This.nametype==1), 1);
    if any(inx)
        name = This.name(This.nametype==1);
        throw( exception.ModelParser.NO_CURRENT_DATE_IN_STEADY, ...
            name{inx} );
    end
end

% No transition variable in some transition equations
%------------------------------------------------------
% Dynamic equations.
valid = any( any(occurF(This.eqtntype==2, This.nametype==2,:), 3), 2 );
if any(~valid)
    eqtn = This.eqtn(This.eqtntype==2);
    throw( exception.ModelParser.NO_TRANSITION_VARIABLE_IN_DYNAMIC, ...
        eqtn{~valid} );
end
% Steady equations.
if ~This.IsLinear
    valid = any(This.occurS(This.eqtntype==2, This.nametype==2), 2);
    if any(~valid)
        eqtn = This.eqtn(This.eqtntype==2);
        throw( exception.ModelParser.NO_TRANSITION_VARIABLE_IN_STEADY, ...
            eqtn{~valid} );
    end
end

% No measurement variable in some measurement equations
%-------------------------------------------------------
valid = any( any(occurF(This.eqtntype==1, This.nametype==1,:), 3), 2 );
if any(~valid)
    eqtn = This.eqtn(This.eqtntype==1);
    throw( exception.ModelParser.NO_MEASUREMENT_VARIABLE_IN_DYNAMIC, ...
        eqtn{~valid} );
end
% Steady equations.
if ~This.IsLinear
    valid = any(This.occurS(This.eqtntype==1, This.nametype==1), 2);
    if any(~valid)
        eqtn = This.eqtn(This.eqtntype==1);
        throw( exception.ModelParser.NO_MEASUREMENT_VARIABLE_IN_STEADY, ...
            eqtn{~valid} );
    end
end

% # transition equations ~= # transition variables
%--------------------------------------------------
ne = sum(This.eqtntype==2);
nv = sum(This.nametype==2);
if ne~=nv
    throw( exception.ModelParser.NUMBER_TRANSITION_EQUATIONS_VARIABLES, ...
        ne, nv ); %#ok<GTARG>
end

% # measurement equations ~= # measurement variables
%----------------------------------------------------
ne = sum(This.eqtntype==1);
nv = sum(This.nametype==1);
if ne~=nv
    throw( exception.ModelParser.NUMBER_MEASUREMENT_EQUATIONS_VARIABLES, ...
        ne, nv ); %#ok<GTARG>
end

end
